#include "params.h"

#include "fuel_cell_infos.h"
#include "physical_constants.h"
#include "list.h"
#include "matrix.h"
//~ #include "mpi_tools.h"
#include "opencl_tools.h"
#include "point.h"

#define PLAQUE 1
#define COOLING 2
#define CELL 3

int schemaSize = 2;
int fuelCellSchema[] = {CELL, PLAQUE, 0 };

#define NBNODES_PLAQUE_X 11
#define NBNODES_ELEM_X 13
#define NBNODES_REAC_X 3
#define NBNODES_FICTIF_X 1

#define NBNODES_CANAL_Y 17
#define NBNODES_VOILE_Y 11
#define NBNODES_FICTIF_Y 1

// Description des noeuds.

ListInt blockNbNodesXPlaque = {"nbNodesXPlaque", 2, {NBNODES_PLAQUE_X, NBNODES_FICTIF_X, EOLIST} };
ListInt blockNbNodesXCooling = {"nbNodesXCooling", 2, {NBNODES_PLAQUE_X, NBNODES_FICTIF_X, EOLIST}};
ListInt blockNbNodesXCell = {"nbNodesXCell", 17,
    {NBNODES_FICTIF_X, NBNODES_PLAQUE_X, NBNODES_FICTIF_X, NBNODES_PLAQUE_X, NBNODES_FICTIF_X,
       NBNODES_ELEM_X, NBNODES_FICTIF_X, NBNODES_REAC_X, NBNODES_FICTIF_X, NBNODES_ELEM_X,
       NBNODES_FICTIF_X, NBNODES_REAC_X, NBNODES_FICTIF_X, NBNODES_ELEM_X, NBNODES_FICTIF_X,
       NBNODES_PLAQUE_X, NBNODES_FICTIF_X, EOLIST}};

ListInt *blockNbNodesXSchema[3] = {&blockNbNodesXPlaque, &blockNbNodesXCooling, &blockNbNodesXCell};

ListInt *blockNbNodesX = NULL;
ListInt *blockNbNodesY = NULL;
ListInt *blockNbNodesZ = NULL;

// #define CANAL_SIZE_Z            0.114     // [m]

tData blockSizeXElems[20][20]={{PLAQUE_SIZE_X, FICTIF_SIZE_X},
        {CANAL_SIZE_X, FICTIF_SIZE_X},
        {FICTIF_SIZE_X, PLAQUE_SIZE_X, FICTIF_SIZE_X, CANAL_SIZE_X, FICTIF_SIZE_X, ANODE_SIZE_X, FICTIF_SIZE_X,
    ANODE_REACT_SIZE_X, FICTIF_SIZE_X, MEMBRANE_SIZE_X, FICTIF_SIZE_X, CATHODE_REACT_SIZE_X, FICTIF_SIZE_X,
    CATHODE_SIZE_X, FICTIF_SIZE_X, CANAL_SIZE_X, FICTIF_SIZE_X}
};

tData *blockSizeX = NULL;
tData *blockSizeY = NULL;
tData *blockSizeZ = NULL;

// Materials
Material tMaterial[MAT_END] = {
    // Name    K [W/(m.K)] CP[J/(kg.K)] RHO[Kg.m^-3] H[W/m2/K] Tinit Type
    {"FICTIF",          0.,    4180.,    1000.,      0.,    353.,  TYPE_SOLID},
    {"PLAQUE",      110.00,     708.,    2267.,      0.,    353.,  TYPE_SOLID},
    {"ANODE",          0.2,     710.,    1300.,      0.,    353.,  TYPE_SOLID},
    {"ANODE_REACT",    0.2,     710.,    1300.,      0.,    353.,  TYPE_SOLID},
    {"MEMBRANE",       .34,   68.175,    2050.,      0.,    353.,  TYPE_SOLID},
    {"CATHODE",        0.2,     710.,    1300.,      0.,    353.,  TYPE_SOLID},
    {"CATHODE_REACT",  0.2,     710.,    1300.,      0.,    353.,  TYPE_SOLID},
    {"H2",          0.1897,   14304.,  0.08988,     73.,    343.,  TYPE_GAZ},
    {"AIR",          0.024,    1009.,    0.909,   76.17,    343.,  TYPE_GAZ},
    {"OUTSIDE",         0.,     666.,     666.,     10.,    333.,  TYPE_GAZ},
    {"COOLER",          0.,    4180.,    1000.,    800.,    343.,  TYPE_LIQUID}
};

// Temps

tData deltaT = 0.000003;
// tData deltaT = 0.00000001;

// Gaz !
tData gazOrientation[] = {1, 0, 1, 0, 1, 0};

Point3 mapSize;
void InitBorder(ListInt **blockNbNodes, tData **blockSize, int size, char *str);
int min(int a, int b){
    if( a <= b )
        return a;
    return b;
}

void BuildFuelCell(int *granularity, int *schema, int schemaSize, int y){
    int nbNodes;
    double size;

    mapSize.x = 2;
    mapSize.y = y * 2 + 9;
    mapSize.z = 5;

    for(int i=0; i<schemaSize; i++)
        mapSize.x += blockNbNodesXSchema[fuelCellSchema[i]-1]->size;

    InitBorder(&blockNbNodesX, &blockSizeX, mapSize.x, "Nb_X");
    InitBorder(&blockNbNodesY, &blockSizeY, mapSize.y, "Nb_Y");
    InitBorder(&blockNbNodesZ, &blockSizeZ, mapSize.z, "Nb_Z");

    int posX = 1;
    for(int i=0; i<schemaSize; i++){
        int type = fuelCellSchema[i]-1;
        for( int a=0; a<blockNbNodesXSchema[type]->size; a++){
            nbNodes = blockNbNodesXSchema[type]->list[a];
            nbNodes = min(nbNodes, granularity[0]);

            blockNbNodesX->list[posX] = nbNodes;
            blockSizeX[posX++] = blockSizeXElems[type][a];
        }
    }
    int lastWasGazLine = 0;
    for( y=1; y<mapSize.y - 1; y++ ){
        if(y == 2 || y == (mapSize.y - 3)){
           nbNodes = NBNODES_VOILE_Y;
           size = PLAQUE_SIZE_Y;
        }else{
          if( y % 2 == 1){
              nbNodes = NBNODES_FICTIF_Y;
              size = FICTIF_SIZE_Y;
          }else{
            if( lastWasGazLine ){
                nbNodes = NBNODES_VOILE_Y;
                size = PLAQUE_SIZE_Y;
                lastWasGazLine = 0;
            }else{
                nbNodes = NBNODES_CANAL_Y;
                size = CANAL_SIZE_Y;
                lastWasGazLine = 1;
            }
          }
        }
        nbNodes = min(nbNodes, granularity[1]);

        blockNbNodesY->list[y] = nbNodes;
        blockSizeY[y] = size;
    }

        blockNbNodesZ->list[1] = 1;
        blockSizeZ[1] = 0.0;

        blockNbNodesZ->list[mapSize.z -2] = 1;
        blockSizeZ[mapSize.z - 2] = 0.0;

    for(int z=2; z<mapSize.z - 2; z++ ){
        nbNodes = NBNODES_Z;
        size = CANAL_SIZE_Z;
        nbNodes = min(nbNodes, granularity[2]);

        blockNbNodesZ->list[z] = nbNodes;
        blockSizeZ[z] = size;
    }
}

void InitBorder(ListInt **blockNbNodes, tData **blockSize, int size, char *str){
    *blockNbNodes = ListAllocate(str, size);
    *blockSize = (tData*)malloc(size * sizeof(tData));

    (*blockSize)[0] = 1;
    (*blockSize)[size - 1] = 1;
    (*blockNbNodes)->list[0] = 1;
    (*blockNbNodes)->list[size - 1] = 1;
    (*blockNbNodes)->list[size] = EOLIST;
}


void ComputeGlobalMatrixSize(){
    nbNodes.x = blockNbNodesX->size;
    nbNodes.y = blockNbNodesY->size;
    nbNodes.z = blockNbNodesZ->size;

    ListPrint( blockNbNodesX );
    matrixSize.x = ListSum( blockNbNodesX );
    matrixSize.y = ListSum( blockNbNodesY );
    matrixSize.z = ListSum( blockNbNodesZ );
}

void ComputeDeltaT(){
    tData min = 100;
    for(int x=0; x<blockNbNodesX->size; x++){
        tData value = blockSizeX[x] / blockNbNodesX->list[x];
        if( value > 0 && min > value )
            min = value;
    }

    for(int y=0; y<blockNbNodesY->size; y++){
        tData value = blockSizeY[y] / blockNbNodesY->list[y];
        if( value > 0 && min > value )
            min = value;
    }

    for(int z=0; z<blockNbNodesZ->size; z++){
        tData value = blockSizeZ[z] / blockNbNodesZ->list[z];
        if( value > 0 && min > value )
            min = value;
    }

    deltaT = min / 128.;

    messagecl("DeltaT = %e; min = %f", deltaT, min);
}

void InitFuelCell(int *granularity){
    BuildFuelCell(granularity, fuelCellSchema, schemaSize, 12);
    ComputeGlobalMatrixSize();
    ComputeDeltaT();
    fuelCellMap = (int ***)Matrix3Allocate(zero3, nbNodes, sizeof(int));

    for(int x = 0; x < nbNodes.x; x++)
        for(int y = 0; y < nbNodes.y; y++)
            for(int z = 0; z < nbNodes.z; z++)
                if( x == 0 || x == nbNodes.x - 1 ||
                        y == 0 || y == nbNodes.y - 1 ||
                        z == 0 || z == nbNodes.z - 1)
                    fuelCellMap[x][y][z] = MAT_OUTSIDE;

    int x = 1;
    for(int i = 0; i < schemaSize; i++){
        int type = fuelCellSchema[i];
        for( int a=0; a<blockNbNodesXSchema[type - 1]->size; a++){
            int lastWasGazLine = 0;
            int lastWasCooler = 1;
            for(int y = 1; y < nbNodes.y - 1; y++){
                int mat = -1;

                if( type == PLAQUE ){
                    if( a == 0 )
                    {
                        if( y % 2 == 1 )
                            mat = MAT_FICTIF;
                         else
                            mat = MAT_PLAQUE;
                    }
                    else
                        mat = MAT_FICTIF;
                }
                if( type == COOLING ){
                    if( y % 2 == 1){
                      mat = MAT_FICTIF;
                    }else{
                      if( !lastWasCooler ){
                          mat = MAT_COOLER;
                          lastWasCooler = 1;
                      }else{
                          mat = MAT_PLAQUE;
                          lastWasCooler = 0;
                      }
                    }
                }
                if( type == CELL ){
                    int tab[4][20] = {{MAT_FICTIF, MAT_FICTIF, MAT_FICTIF, MAT_FICTIF, MAT_FICTIF, MAT_FICTIF, MAT_FICTIF, MAT_FICTIF, MAT_FICTIF, MAT_FICTIF, MAT_FICTIF, MAT_FICTIF, MAT_FICTIF, MAT_FICTIF, MAT_FICTIF, MAT_FICTIF, MAT_FICTIF},
                        {MAT_FICTIF, MAT_PLAQUE, MAT_FICTIF, MAT_PLAQUE, MAT_FICTIF, MAT_PLAQUE, MAT_FICTIF, MAT_PLAQUE, MAT_FICTIF, MAT_PLAQUE, MAT_FICTIF, MAT_PLAQUE, MAT_FICTIF, MAT_PLAQUE, MAT_FICTIF, MAT_PLAQUE, MAT_FICTIF },
                        {MAT_FICTIF, MAT_PLAQUE, MAT_FICTIF, MAT_H2, MAT_FICTIF, MAT_ANODE, MAT_FICTIF, MAT_ANODE_REACT, MAT_FICTIF, MAT_MEMBRANE, MAT_FICTIF, MAT_CATHODE_REACT, MAT_FICTIF, MAT_CATHODE, MAT_FICTIF, MAT_H2, MAT_FICTIF},
                        {MAT_FICTIF, MAT_PLAQUE, MAT_FICTIF, MAT_PLAQUE, MAT_FICTIF, MAT_ANODE, MAT_FICTIF, MAT_ANODE_REACT, MAT_FICTIF, MAT_MEMBRANE, MAT_FICTIF, MAT_CATHODE_REACT, MAT_FICTIF, MAT_CATHODE, MAT_FICTIF, MAT_PLAQUE, MAT_FICTIF}};
                    if(y == 2 || y == (nbNodes.y - 3)){
                      mat = tab[1][a];
                    }else{
                      if(y%2 == 1){
                        mat = tab[0][a];
                      }else{
                        if(!lastWasGazLine){
                          mat = tab[2][a];
                          lastWasGazLine = 1;
                        }else{
                          lastWasGazLine = 0;
                          mat = tab[3][a];
                        }
                      }
                    }
                }

                if( mat == -1 )
                    die_cl("Erreur mat.");

                fuelCellMap[x][y][1] = MAT_FICTIF;
                fuelCellMap[x][y][nbNodes.z - 2] = MAT_FICTIF;

                for(int z = 2; z < nbNodes.z - 2; z++){
                    int matType = tMaterial[mat].type;
                    if( matType == TYPE_GAZ || matType == TYPE_LIQUID ){
                        fuelCellMap[x][y][0] = mat;
                        fuelCellMap[x][y][nbNodes.z - 1] = mat;
                    }
                    fuelCellMap[x][y][z] = mat;
                }
            }
            x++;
        }
    }
}

